package param

import (
	"net/http"
	"net/url"
	"mime/multipart"
	"reflect"
	"fmt"
	"unsafe"
)

// The whole request body is parsed and up to a total of maxMemory bytes of
// its file parts are stored in memory, with the remainder stored on
// disk in temporary files.
var maxMemory int64 = 32 << 20 // 32 MB

type Where int

const (
	Query Where = iota
	Form
	Path
	Header
	Cookie
)

func (w Where) String() string {
	switch w {
	case Query:
		return "query"
	case Form:
		return "form" // TODO: OpenApi 3.0 没有 form
	case Path:
		return "path"
	case Header:
		return "header"
	case Cookie:
		return "cookie" // TODO: OpenApi 2.0 没有 cookie
	}
	return "query"
}

type Parameter struct {
	// Required.
	// tell the parser where to parse the data.
	In     Where

	// Required.
	// the parameter model.
	Schema interface{}

	// Custom validator
	Validator *Validator

	// Ignoring param
	Ignore []string
}

type Parameters struct {
	parsers map[string]*Parser
	in map[string]Where
	hasFileField bool
}

// 同时注册多个参数模型
func RegisterParameters(ps []*Parameter) *Parameters {
	parameters := &Parameters{
		parsers: make(map[string]*Parser, len(ps)),
		in: make(map[string]Where, len(ps)),
	}
	for _, p := range ps {
		parser := NewParser(p.Schema, NewFieldNameProvider(p.Ignore), p.Validator)
		parameters.in[parser.Name] = p.In
		parameters.parsers[parser.Name] = parser
		if parser.HasFileField() {
			parameters.hasFileField = true
		}
	}
	return parameters
}

func (ps *Parameters) HasFileField() bool {
	return ps.hasFileField
}

// 验证并解析 http 数据, 并保存上传文件. schemas 中的每一个参数模型都必须通过 RegisterParameters() 注册.
func (ps *Parameters) ValidAndParseHttp(req *http.Request, path url.Values, schemas ...interface{}) (msg, id string, err error) {
	var deferFuncs []func() error // 保存文件应当在解析所有字段以及验证所有字段之后执行
	for _, schema := range schemas {
		var form multipart.Form
		v := reflect.ValueOf(schema)
		name := v.Type().String()
		parser, ok := ps.parsers[name]
		if !ok {
			err = fmt.Errorf("unregistered schema %s", name)
			return
		}
		pointer := unsafe.Pointer(v.Pointer())
		switch ps.in[name] {
		case Query:
			if req.Form == nil {
				err := req.ParseForm()
				if err != nil {
					return "", "", err
				}
			}
			form.Value = req.Form
		case Form:
			if ps.hasFileField {
				if req.MultipartForm == nil {
					err := req.ParseMultipartForm(maxMemory)
					if err != nil {
						return "", "", err
					}
				}
				form = *req.MultipartForm
				deferFuncs = append(deferFuncs, func() error {
					return parser.StoreFile(pointer, form)
				})
			} else {
				if req.Form == nil {
					err := req.ParseForm()
					if err != nil {
						return "", "", err
					}
				}
				form.Value = req.Form
			}
		case Path:
			form.Value = path
		case Header:
			form.Value = req.Header
		case Cookie:
			form.Value = make(map[string][]string)
			for _, c := range req.Cookies() {
				form.Value[c.Name] = []string{c.Value}
			}
		}
		msg, id, err = parser.Parse(pointer, form)
		if msg != "" || err != nil {
			return
		}
	}
	// 保存文件
	for _, call := range deferFuncs {
		err = call()
		if err != nil {
			return
		}
	}
	return
}